home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 2
/
Nebula Two.iso
/
SourceCode
/
libcs
/
parsedate.y
< prev
next >
Wrap
Text File
|
1995-06-12
|
35KB
|
1,392 lines
/*
* Copyright (c) 1990 Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND CARNEGIE MELLON UNIVERSITY
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT
* SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Users of this software agree to return to Carnegie Mellon any
* improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*
* Export of this software is permitted only after complying with the
* regulations of the U.S. Deptartment of Commerce relating to the
* Export of Technical Data.
*/
/*
* parsedate - parse date specification. Originally pdate.
*
**********************************************************************
* HISTORY
* $Log: parsedate.y,v $
* Revision 1.4 90/12/11 18:03:19 mja
* Add copyright/disclaimer for distribution.
*
* 20-Jul-88 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Made strings[] array static.
*
* 27-Jan-87 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Recoded currtm initialization. The RT compiler does not allow
* dereferencing a pointer that is the return value of a routine.
*
* 18-Jan-86 Leonard Hamey (lgh) at Carnegie-Mellon University
* Fix bug 'sun jan 12 00:00:00 1986' gives year 1992.
* Also ingore () and [] in date. Other junk chars become JUNK token.
*
* 05-Dec-85 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Added second argument to longjmp (don't know why this worked
* under 4.1...). Also added "this upcoming ..." to mean the
* next occurance of "..." in the future. If "..." is today,
* then the next occurrence is used. Added {April,All} Fool[s]
* [day].
*
* 09-Jul-85 Leonard Hamey (lgh) at Carnegie-Mellon University
* Fixed to parse a.m. and p.m. properly. These are special
* because the period is a delimiter and creates two tokens.
* While we are at it, we handle "a m " and variants.
*
* 02-Jan-85 Leonard Hamey (lgh) at Carnegie-Mellon University
* Added parsing of "months" and "years" relative constructs.
* (e.g. 3 months before today, 2 years from tomorrow.)
* Added "ago" constructs (e.g. 3 days ago.).
*
* 08-Mar-84 Leonard Hamey (lgh) at Carnegie-Mellon University
* Bug fixes, code tidying.
*
* 16-Feb-84 Leonard Hamey (lgh) at Carnegie-Mellon University
* Modified pre-parser to allow period following an abbreviation.
*
* 03-Feb-84 Leonard Hamey (lgh) at Carnegie-Mellon University
* Corrected bugs when time preceded date. Introduced code to
* increment date if time >= 24:00. Changed NIGHT to mean 5pm
* or later. Changed evening to mean 3pm or later. Fixed MIDNIGHT bug.
* Allowed parsing 12:00 noon. Handled military time: 0530.
*
* The grammar now compiles with 3 shift/reduce conflicts and 2
* reduce/reduce. The reduce/reduce are due to the keyword THE and
* dont matter. Two shift/reduce are due to nnnn being a time or a
* year. Yacc chooses to take it as a year, which is what we want.
*
* 16-Jan-84 Leonard Hamey (lgh) at Carnegie-Mellon University
* Included code to handle words other than months and days.
* Revamped grammar to handle all sorts of reasonable date constructs
* such as "Wednesday", "This Thursday", "A week from today", etc.
* Modified arguments to allow specification of past/future
* dates. Defined the existing contents of tm structure to mean
* NOW. Returned structure may include values of -1 for unspecified
* fields. Also, if date is properly specified then weekday and yearday
* is always computed. (Omitted year is internally represented by NOYEAR)
* Increased character limit to 80 characters. Introduced
* parsedate () with more arguments than pdate ().
*
* 01-Jan-83 Mike Accetta (mja) at Carnegie-Mellon University
* Fixed bug in month number recording which neglected to subtract one
* from month numbers specified in the form mm/dd/yy form before storing
* in the tm_mon field.
*
* 21-Feb-80 Mike Accetta (mja) at Carnegie-Mellon University
* Fixed bug in grammar which failed to parse dates followed by
* times when no year was specified in the date; changed
* date string limit from 25 to 50 characters.
*
* 03-Jan-80 Mike Accetta (mja) at Carnegie-Mellon University
* Created.
*
**********************************************************************
*/
%start date_time
%token NUMBER NUMBER4 WEEKDAY MONTH HOUR SHOUR
%token TODAY NOW TONIGHT NEXT THIS DAY WEEK FORTNIGHT UPCOMING
%token EVERY FROM BEFORE THE A AT ON LAST AFTER IN OF AGO WORD_MONTH
%token NOON AMPM TIMEKEY
%token AND NWORD NTHWORD ST ND RD TH
%token CHRISTMAS NEW YEAR ALL FOOLS
%token JUNK
/* Resolve shift/reduce conflicts on THE */
%left THE
%{
#include <sys/types.h>
#include <setjmp.h>
#include <sys/time.h>
#include <c.h>
# define MAXPEEP 3
# define NDTMS 10
# define NOYEAR -1901
/* PTM: indicates ptm */
# define PTM -1
/* CURRTM: indicates current time */
# define CURRTM -2
/* CURRDATE: indicates current date (time set to -1). */
# define CURRDATE -3
/* PAST, FUTURE: logical values for past and future */
# define PAST 1
# define FUTURE 0
static int junk_err; /* junk token give error? */
static int ppf; /* past, or future */
static int nottoday; /* future doesn't include today */
static struct tm scurrtm;
static struct tm *currtm = &scurrtm; /* current time */
static char *strp; /* current position in date string */
static int delim; /* previous field delimiter */
static jmp_buf errbuf; /* jump location for parse errors */
extern char *nxtarg();
struct stable { char *text; int token; int lexval; };
static int shour, tmkey;
static struct tm ptm;
static int pcount;
static int result;
/* Date structure for constraining dates */
struct dtm
{ int repn; /* Current representation */
int days;
struct tm tm;
int count;
};
static struct dtm dtm[NDTMS];
static int dtmused[NDTMS];
/* Representations */
# define RDAYS 0
# define RTM 1
/*
* Month and week day names (upper case only) and other words.
*/
static
struct stable strings[] =
{
{ "JAN*UARY", MONTH, 0 }, /* months (0-11) */
{ "FEB*RUARY", MONTH, 1 },
{ "MAR*CH", MONTH, 2 },
{ "APR*IL", MONTH, 3 },
{ "MAY", MONTH, 4 },
{ "JUN*E", MONTH, 5 },
{ "JUL*Y", MONTH, 6 },
{ "AUG*UST", MONTH, 7 },
{ "SEP*TEMBER", MONTH, 8 },
{ "OCT*OBER", MONTH, 9 },
{ "NOV*EMBER", MONTH, 10 },
{ "DEC*EMBER", MONTH, 11 },
{ "SUN*DAY", WEEKDAY, 0 }, /* days of the week (0-6) */
{ "MON*DAY", WEEKDAY, 1 },
{ "TUE*SDAY", WEEKDAY, 2 },
{ "WED*NESDAY", WEEKDAY, 3 },
{ "THU*RSDAY", WEEKDAY, 4 },
{ "FRI*DAY", WEEKDAY, 5 },
{ "SAT*URDAY", WEEKDAY, 6 },
{ "YESTERDAY", TODAY, -1 }, /* relative to today */
{ "TODAY", TODAY, 0 },
{ "TONIGHT", TONIGHT, 0 },
{ "NOW", NOW, 0 },
{ "AGO", AGO, 0 },
{ "TOMORROW", TODAY, 1 },
{ "NEXT", NEXT, 0 }, /* keywords */
{ "THIS", THIS, 0 },
{ "UPCOMING", UPCOMING, 0 },
{ "DAY*S", DAY, 0 },
{ "WEEK*S", WEEK, 0 },
{ "MONTH*S", WORD_MONTH, 0 },
{ "YEAR*S", YEAR, 0 },
{ "ALL", ALL, 0 },
{ "FOOL*S", FOOLS, 0 },
{ "FORTNIGHT", FORTNIGHT, 0 }, /* two weeks (Australian) */
/* { "EVERY", EVERY, 0 }, */
{ "FROM", FROM, 0 },
{ "AFTER", AFTER, 0 },
{ "BEFORE", BEFORE, 0 },
{ "LAST", LAST, 0 },
{ "THE", THE, 0 },
{ "A", A, 0 },
{ "AT", AT, 0 },
{ "ON", ON, 0 },
{ "IN", IN, 0 },
{ "OF", OF, 0 },
/* { "AND", AND, 0 }, */
{ "MORNING", TIMEKEY, 0 }, /* time keywords. Morning is 0:00 - 11:59 */
{ "AFTERNOON", TIMEKEY, 12 }, /* Afternoon is 12:00 - 23:59 */
{ "EVENING", TIMEKEY, 15 }, /* Evening is 15:00 - 02:59 */
{ "NIGHT", TIMEKEY, 17 }, /* Night is 17:00 - 04:59 */
{ "NOON", NOON, 12 }, /* time specifications */
{ "MIDNIGHT", NOON, 24 },
{ "ONE", NWORD, 1 }, /* numbers up to 19 */
{ "TWO", NWORD, 2 },
{ "THREE", NWORD, 3 },
{ "FOUR", NWORD, 4 },
{ "FIVE", NWORD, 5 },
{ "SIX", NWORD, 6 },
{ "SEVEN", NWORD, 7 },
{ "EIGHT", NWORD, 8 },
{ "NINE", NWORD, 9 },
{ "TEN", NWORD, 10 },
{ "ELEVEN", NWORD, 11 },
{ "TWELVE", NWORD, 12 },
{ "THIRTEEN", NWORD, 13 },
{ "FOURTEEN", NWORD, 14 },
{ "FIFTEEN", NWORD, 15 },
{ "SIXTEEN", NWORD, 16 },
{ "SEVENTEEN", NWORD, 17 },
{ "EIGHTEEN", NWORD, 18 },
{ "NINETEEN", NWORD, 19 },
{ "FIRST", NTHWORD, 1 }, /* number up to 19th */
{ "SECOND", NTHWORD, 2 },
{ "THIRD", NTHWORD, 3 },
{ "FOURTH", NTHWORD, 4 },
{ "FIFTH", NTHWORD, 5 },
{ "SIXTH", NTHWORD, 6 },
{ "SEVENTH", NTHWORD, 7 },
{ "EIGHT", NTHWORD, 8 },
{ "NINTH", NTHWORD, 9 },
{ "TENTH", NTHWORD, 10 },
{ "ELEVENTH", NTHWORD, 11 },
{ "TWELFTH", NTHWORD, 12 },
{ "THIRTEENTH", NTHWORD, 13 },
{ "FOURTEENTH", NTHWORD, 14 },
{ "FIFTEENTH", NTHWORD, 15 },
{ "SIXTEENTH", NTHWORD, 16 },
{ "SEVENTEENTH", NTHWORD, 17 },
{ "EIGHTEENTH", NTHWORD, 18 },
{ "NINETEENTH", NTHWORD, 19 },
{ "ST", ST, 0 }, /* for 1st */
{ "ND", ND, 0 }, /* 2nd */
{ "RD", RD, 0 }, /* 3rd */
{ "TH", TH, 0 }, /* nth */
{ "AM", AMPM, 0 }, /* time qualifiers */
{ "A.M.", AMPM, 0 },
{ "PM", AMPM, 12 },
{ "P.M.", AMPM, 12 },
{ "CHRISTMAS", CHRISTMAS, 1225 }, /* special dates */
{ "NEW", NEW, 101 },
{ 0, 0, 0 }
};
%}
%%
/* Grammar for legal dates. The date is returned in the dtm structure indexed
* by result. The time is returned in ptm (and, possibly, shour).
*/
date_time : sdate_time
{ result = $$;
check ($$); }
;
sdate_time : stime
{ $$ = new_dtm (CURRDATE); }
| full_date time
| full_date time year
/*
* This is a hack. To handle the strange format
* day month day-in-month time year
* It parses the date as though it were a full
* specification and then substitutes the year.
*/
{ setrep (&(dtm[$$]), RTM);
dtm[$$].tm.tm_year = ptm.tm_year;
dtm[$$].tm.tm_wday = -1;
dtm[$$].tm.tm_yday = -1; }
| time ON full_date
{ $$ = $3; }
| time full_date
{ $$ = $2; }
| full_date
| time
{ $$ = new_dtm (CURRDATE); }
| stime today
{ $$ = new_dtm (CURRDATE);
incr ($$, $2); }
| NOW
{ $$ = new_dtm (CURRDATE);
ptm.tm_hour = currtm->tm_hour;
ptm.tm_min = currtm->tm_min;
ptm.tm_sec = currtm->tm_sec; }
;
full_date : rec_date
| timekey _of rec_date
{ $$ = $3; }
| THE timekey _of rec_date
{ $$ = $4; }
| rec_date timekey
;
rec_date : date
{ check ($$); }
;
date : partial_date
{ $$ = new_dtm (CURRDATE);
constrain (PTM, $$, ppf, 1); }
| THIS UPCOMING relative_partial_date
{ $$ = new_dtm (CURRDATE);
nottoday = 1;
constrain (PTM, $$, FUTURE, 1); }
| THIS relative_partial_date
{ $$ = new_dtm (CURRDATE);
constrain (PTM, $$, FUTURE, 1); }
| NEXT relative_partial_date
{ $$ = new_dtm (CURRDATE);
constrain (PTM, $$, FUTURE, 2); }
| LAST relative_partial_date
{ $$ = new_dtm (CURRDATE);
incr ($$, -1);
constrain (PTM, $$, PAST, 1); }
| weekday WEEK
{ $$ = new_dtm (CURRDATE);
constrain (PTM, $$, FUTURE, 1);
incr ($$, 7); }
| weekday FORTNIGHT
{ $$ = new_dtm (CURRDATE);
constrain (PTM, $$, FUTURE, 1);
incr ($$, 14); }
| adjusted_date
| today
{ $$ = new_dtm (CURRDATE);
incr ($$, $1); }
| TODAY WEEK
{ $$ = new_dtm (CURRDATE);
incr ($$, $1 + 7); }
| TODAY FORTNIGHT
{ $$ = new_dtm (CURRDATE);
incr ($$, $1 + 14); }
| stddate
{ $$ = new_dtm (PTM); }
| weekday stddate
{ $$ = new_dtm (PTM); }
;
stddate : yearday year
| yearday '-' year
| yearday '/' year
;
/* The following rule must precede other uses of relative_yearday and
* nonrelative_yearday so that the preferred reduction is to yearday.
* This, in turn, causes a 4-digit number to be preferred as a year
* rather than military time. */
yearday : relative_yearday
| nonrelative_yearday
;
relative_partial_before : relative_partial_dtm
| LAST relative_partial_date
{ $$ = new_dtm (PTM); }
| nth LAST weekday
{ $$ = new_dtm (PTM);
dtm[$$].count = $1; }
;
relative_partial_dtm : relative_partial_date
{ $$ = new_dtm (PTM); }
| nth weekday
{ $$ = new_dtm (PTM);
dtm[$$].count = $1; }
;
/*
* partial_date: Returns ptm.
*/
partial_date : relative_partial_date
| nonrelative_yearday
;
relative_partial_date : relative_yearday
| weekday
| nth
{ ptm.tm_mday = $1; }
| weekday nth
{ ptm.tm_mday = $2; }
| weekday relative_yearday
;
weekday : WEEKDAY
{ ptm.tm_wday = $1; }
;
holiday : sholiday
{ ptm.tm_mon = $1/100-1; ptm.tm_mday = $1%100; }
;
sholiday : CHRISTMAS
| CHRISTMAS DAY
| NEW YEAR
| ALL FOOLS
{ $$ = 401; }
| ALL FOOLS DAY
{ $$ = 401; }
| MONTH FOOLS
{ if ($1 != 3) yyerror ();
$$ = 401; }
| MONTH FOOLS DAY
{ if ($1 != 3) yyerror ();
$$ = 401; }
;
/*
* forward_rec_date: Returns index to dtm.
*/
forward_rec_date : AFTER rec_date
{ $$ = $2; }
| FROM rec_date
{ $$ = $2; }
;
/*
* adjusted_date: Returns index to dtm.
*/
adjusted_date : days forward_rec_date
{ $$ = $2;
incr ($$, $1); }
| days BEFORE rec_date
{ $$ = $3;
incr ($$, -$1); }
| days AGO
{ $$ = new_dtm (CURRTM);
incr ($$, -$1); }
| months forward_rec_date
{ $$ = $2;
incrmonth ($$, $1); }
| months BEFORE rec_date
{ $$ = $3;
incrmonth ($$, -$1); }
| months AGO
{ $$ = new_dtm (CURRDATE);
incrmonth ($$, -$1); }
| years forward_rec_date
{ $$ = $2;
incryear ($$, $1); }
| years BEFORE rec_date
{ $$ = $3;
incryear ($$, -$1); }
| years AGO
{ $$ = new_dtm (CURRDATE);
incryear ($$, -$1); }
| rel_date
| THE rel_date
{ $$ = $2; }
;
/*
* rel_date: Parse date relative to another date. Returns index to
* dtm structure containing the resolved date.
*/
rel_date : relative_partial_dtm AFTER rec_date
{ $$ = $3;
incr ($$, 1);
constrain ($1, $$, FUTURE, dtm[$1].count); }
| relative_partial_before BEFORE rec_date
{ $$ = $3;
incr ($$, -1);
constrain ($1, $$, PAST, dtm[$1].count); }
;
/*
* today: Parse an indication of the current day.
* Side effect: sets tmkey if time of day indication is also made.
*/
today : TODAY
| TONIGHT
{ tmkey = 17; }
| THIS timekey
| TODAY timekey
{ if ($1 == 0) yyerror (); }
;
/*
* months: Parse a number of months. Returns integer the number of months.
*/
months : number WORD_MONTH
{ $$ = $1; }
| A WORD_MONTH
{ $$ = 1; }
;
/*
* years: Parse a number of years. Returns integer the number of years.
*/
years : number YEAR
{ $$ = $1; }
| A YEAR
{ $$ = 1; }
;
/*
* days: Parse a number of days. Returns integer number of days.
*/
days : number DAY
| nth_day
| the_nth_day
| THE DAY
{ $$ = 1; }
| A DAY
{ $$ = 1; }
| DAY
{ $$ = 1; }
| number WEEK
{ $$ = $1 * 7; }
| A WEEK
{ $$ = 7; }
| WEEK
{ $$ = 7; }
| A FORTNIGHT
{ $$ = 14; }
| FORTNIGHT
{ $$ = 14; }
;
/*
* nonrelative_yearday: Parse day of the year.
* Returns parsed specification in ptm.
*/
nonrelative_yearday : month_name THE nth
{ ptm.tm_mday = $3; }
| stdmonthday _of month_name
| THE stdmonthday _of month_name
| the_nth_day OF month_name
{ ptm.tm_mday = $1; }
| nth_day OF month_name
{ ptm.tm_mday = $1; }
;
/*
* relative_yearday: Parse day within year. Return ptm.
*/
relative_yearday : month '/' monthday
| month_name monthday
| month_name '-' monthday
| stdmonthday '-' month_name
| stdmonthday '-' month
| holiday
;
/*
* time: Returns ptm containing the parsed time spec.
*/
time : ttime
| AT ttime
| AT stime
;
stime : simple_time
| simple_time IN THE timekey
;
simple_time : number
{ if ($1 < 1 || $1 > 12) yyerror ();
shour = $1; }
;
timekey : TIMEKEY
{ tmkey = $1; }
;
/* 12am is midnight. i.e. 0:00. 12pm is noon. */
ttime : hm_time
| hm_time IN THE timekey
| hour ':' min ':' sec
| whour AMPM
{ tmkey = $2; }
| hm_time AMPM
{ tmkey = $2; }
| NOON
{ ptm.tm_hour = $1; }
| whour NOON
{ if ($1 != 12) yyerror ();
shour = -1;
ptm.tm_hour = $2; }
| hm_time NOON
{ if (shour != 12 || ptm.tm_min != 0) yyerror ();
shour = -1;
ptm.tm_hour = $2; }
| NUMBER4
{ ptm.tm_hour = $1 / 100;
ptm.tm_min = $1 % 100;
if (ptm.tm_min > 59) yyerror ();
if ($1 > 2400) yyerror (); }
;
hm_time : hour ':' min
| hour '.' min
;
month_name : MONTH
{ ptm.tm_mon = $1; }
;
/*
* year: Parse year specification.
* Returns ptm.tm_year set to the year specified.
*/
year : NUMBER
{ ptm.tm_year = $1 + (($1>=100)?-1900:0); }
| NUMBER4
{ ptm.tm_year = $1 - 1900; }
| THIS YEAR
{ ptm.tm_year = currtm->tm_year; }
| LAST YEAR
{ ptm.tm_year = currtm->tm_year - 1; }
| NEXT YEAR
{ ptm.tm_year = currtm->tm_year + 1; }
;
/*
* month: Parse numeric month. Store (0 origin) in ptm.tm_mon.
*/
month : NUMBER
{ ptm.tm_mon = $1 - 1; }
;
monthday : NWORD
{ ptm.tm_mday = $1; }
| stdmonthday
;
stdmonthday : NUMBER
{ ptm.tm_mday = $1; }
| nth
{ ptm.tm_mday = $1; }
;
hour : HOUR
{ ptm.tm_hour = $1; }
| SHOUR
{ shour = $1; }
;
whour : hour
| NWORD
{ shour = $1; }
;
min : NUMBER
{ ptm.tm_min = $1; }
| HOUR
{ ptm.tm_min = $1; }
| SHOUR
{ ptm.tm_min = $1; }
;
sec : NUMBER
{ ptm.tm_sec = $1; }
;
number : NWORD
| NUMBER
;
/*
* nth: Parse 1st 2nd etc or first second etc.
* Return integer value of the number.
*/
nth : NTHWORD
| NUMBER ST
{ if ($1 % 10 != 1 || $1 % 100 == 11) yyerror ();
$$ = $1; }
| NUMBER ND
{ if ($1 % 10 != 2 || $1 % 100 == 12) yyerror ();
$$ = $1; }
| NUMBER RD
{ if ($1 % 10 != 3 || $1 % 100 == 13) yyerror ();
$$ = $1; }
| NUMBER TH
{ if (($1 + 9) % 10 <= 2 && ($1 % 100) / 10 != 1)
yyerror ();
$$ = $1; }
;
_of : OF
|
;
the_nth_day : THE nth_day
{ $$ = $2; }
;
nth_day : nth DAY
;
%%
parsedate (str, tmp, settm, select, err)
char *str;
struct tm *tmp;
int settm, select, err;
{ long time (), curtim;
struct tm *localtime ();
char tstr[81];
int i;
register struct tm *rtm;
junk_err = err;
if (settm)
{ curtim = time (0);
rtm = localtime (&curtim);
*currtm = *rtm;
}
else
{ *currtm = *tmp;
}
/* initialize lexical analyzer */
for (i = 0; i < NDTMS; i++)
dtmused[i] = 0;
strcpyn(strp = tstr, str, 80);
tstr[80] = 0;
ptm.tm_year = NOYEAR;
ptm.tm_mon = -1;
ptm.tm_mday = -1;
ptm.tm_wday = -1;
ptm.tm_yday = -1;
ptm.tm_hour = -1;
ptm.tm_min = -1;
ptm.tm_sec = -1;
pcount = 0;
currtm->tm_yday = currtm->tm_wday = -1;
shour = -1;
tmkey = -1;
delim = 0;
ppf = select;
nottoday = 0;
if (setjmp(errbuf) == 0)
{
yyparse();
rtm = &(dtm[result].tm);
setrep (&dtm[result], RTM);
rtm->tm_hour = ptm.tm_hour;
rtm->tm_min = ptm.tm_min;
rtm->tm_sec = ptm.tm_sec;
time_def (rtm, currtm);
/* If time is 24:00 or later, advance date to next day. */
if (rtm->tm_hour >= 24)
{ incr (result, rtm->tm_hour / 24);
setrep (&dtm[result], RTM);
rtm->tm_hour = rtm->tm_hour % 24;
}
if (dtm[result].repn == RTM && dtm[result].tm.tm_yday == -1)
setrep (&dtm[result], RDAYS);
setrep (&dtm[result], RTM);
*tmp = dtm[result].tm;
return(0); /* return here on successful parse */
}
else
return(CERROR); /* return here on error */
}
/*
* yyerror - error routine (called by yyparse)
*
* Performs a jump to the error return location established
* by pdate().
*/
static yyerror()
{
longjmp(errbuf, 1);
}
static int prelval;
static struct { int token; int val; } peep[MAXPEEP];
static int peepb = 0, peepe = 0;
static int lead0;
/* yylex - return next token in date string.
*
* Obtains the tokens from nextlex() and returns them. Checks for
* NUMBER tokens which are really HOURs. This is done by peeping ahead.
* It gives more lookahead than yacc can support.
*/
static int yylex ()
{ register int ctoken, htoken;
ctoken = nextlex();
htoken = lead0 ? HOUR : SHOUR;
if (ctoken == NUMBER && yylval <= 24)
{ /* Possible hour - check it out */
peeper (1); /* Allow 1 token peeping */
if (peep[0].token == AMPM || peep[0].token == NOON)
return (htoken); /* NN AM or NN PM or 12 NOON */
if (peep[0].token == ':' || peep[0].token == '.')
{ peeper (2);
if (peep[1].token == NUMBER)
return (htoken); /* NN:NN or NN.NN */
}
}
return (ctoken);
}
static int nextlex ()
{ register int token;
if (peepb < peepe)
{ token = peep[peepb].token;
yylval = peep[peepb].val;
peepb++;
}
else
{ token = prelex ();
yylval = prelval;
}
return (token);
}
static peeper (n)
int n;
{ register int i;
if (peepb != 0)
{ for (i = peepb; i < peepe; i++)
peep[i-peepb] = peep[i];
peepe -= peepb;
}
peepb = 0;
while (peepe < n)
{ peep[peepe].token = prelex ();
peep[peepe].val = prelval;
peepe++;
}
}
/*
* prelex - return next token in date string
*
* Uses nxtarg() to parse the next field of the date string.
* If a non-space, tab, comma or newline delimiter terminated the
* previous field it is returned before the next field is parsed.
*
* Returns either one of the delimiter characters " -:/.", the token
* from a match on the table (with the associated value in yylval), or
* NUMBER, or JUNK.
* JUNK is any unrecognized token (depending on the call arguments to
* parsedate - an unrecognized token may instead be returned as -1 simply
* terminating the parse.)
* NUMBER is a numeric string. NUMBER4 is a 4-digit number.
* If the numeric string commences with 0, then lead0 is true.
* '@' sign is treated as the token AT.
*/
static int wasabb; /* tabfind indicates abbrev. */
static int prelex()
{
register int ret; /* temp for previous delimiter */
register char *fp; /* current field */
register int find, ndig;
extern char _argbreak; /* current delimiter */
while (1)
{ if (ret=delim)
{
delim = 0;
if (ret == '@')
return (AT);
/*
* Ignore all but the good characters
*/
if (ret != ':' && ret != '/' && ret != '-' && ret != '.')
ret = 0;
if (ret != 0)
return (ret);
}
if (*strp == 0) return (0);
while (*strp == ' ' || *strp == '\t' || *strp == '\n')
strp++;
if (*strp >= '0' && *strp <= '9')
{ prelval = 0;
ndig = 0;
lead0 = *strp == '0';
while (*strp >= '0' && *strp <= '9')
{ prelval = prelval * 10 + *strp - '0';
strp++;
ndig++;
}
if (ndig == 4)
return (NUMBER4);
return (NUMBER);
}
fp = nxtarg (&strp, " \t,-:/.@()[]\n");
delim = _argbreak;
if (*fp == 0 && delim == 0) return (0); /* End of input string */
if (*fp != 0) /* skip null tokens */
{ foldup(fp, fp);
/* Because of the embedded period, a.m. and p.m. do not work.
* Solution is to recognize them explicitly.
*/
if (fp[1] == '\0' && (delim == '.' || delim == ' ') &&
(*fp == 'A' || *fp == 'P') && (*strp == 'M' || *strp == 'm') &&
(strp[1] == '.' || strp[1] == ' ' || strp[1] == '\t' ||
strp[1] == '\0'))
{ /* It is a.m. or p.m. */
prelval = *fp == 'A' ? 0 : 12;
strp += 2; /* Skip the m. */
delim = 0;
return (AMPM);
}
if ((find = tabfind(fp, strings)) >= 0)
{
if (wasabb && delim == '.') /* If tabfind found abbrev */
delim = 0; /* Discard period after abbrev. */
prelval = strings[find].lexval;
return(strings[find].token);
}
if (junk_err)
return (JUNK);
else
return (0);
}
}
}
/* subroutines useful for manipulating dates and used by
* parsedate.y.
*
* Copyright (c) Leonard G C Hamey, December 1983.
*
* date_days (tm): converts the date portion of tm structure to days since
* 1 jan 1900 + 693960. Also can be used to obtain weekday (sunday == 0)
* by taking modulo 7.
*
* days_date: converts days since 1/1/1900 + 693960 to date in tm structre.
*
* tabfind: searches a table of keywords for date parsing.
*
* constrain: fills in default fields under control of past/future
* parameter.
*/
static int date_days (tm)
struct tm *tm;
{ /* Number of days since 1/1/1900 + 693960 */
int dd = tm->tm_mday, mm = tm->tm_mon + 1, yyyy = tm->tm_year + 1900;
int f;
if (mm >= 3)
{ f=365*yyyy+dd+31*mm-31-(4*mm+23)/10+yyyy/4-(75+(yyyy/100)*75)/100;
}
else
{ f=365*yyyy+dd+31*mm-31+(yyyy-1)/4-(75+((yyyy-1)/100)*75)/100;
}
return (f-1);
}
static int monthend[] =
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
static days_date (d, tm)
int d;
struct tm *tm;
{ /* Number of days since 1/1/1900 -> mm/dd/yyyy and weekday */
int t = d - 693960, leap;
int mm, yyyy;
tm->tm_year = 0; /* 1900 */
tm->tm_mon = 0; /* Jan */
tm->tm_mday = 1;
while (t < 0) /* handle dates before 1900 */
{ tm->tm_year += t / 366 - 1;
t = d - date_days (tm);
}
while (t >= 366)
{ tm->tm_year += t / 366;
t = d - date_days (tm);
}
yyyy = tm->tm_year + 1900;
leap = yearsize (yyyy) == 366;
if (! leap && t == 365)
{ tm->tm_year++;
t = 0;
}
tm->tm_yday = t; /* Day in year (0-365) */
if (! leap && t >= 59) /* If after Feb non leap */
t++; /* Fudge as if leap */
mm = t / 31; /* Guess the month */
if (t >= monthend[mm+1]) /* Check the guess */
mm++;
tm->tm_mday = t - monthend[mm] + 1; /* Compute day in month */
tm->tm_mon = mm;
tm->tm_wday = d % 7; /* Sunday = 0 */
}
static int tabfind (text, table)
char *text;
struct stable *table;
{ int tp;
char *tep, *txp;
int find = -1, foundstar;
wasabb = 0;
for (tp = 0; table[tp].text; tp++)
{ tep = table[tp].text;
txp = text;
foundstar = 0;
while (1)
{ if (*tep == '*')
{ foundstar = 1;
tep++;
}
if (! *txp) /* If end of text */
{ if (! *tep) /* If also end of table entry */
return (tp); /* then found */
if (foundstar)
{ if (find >= 0)
return (-2); /* Ambiguous */
find = tp; /* Remember partial match */
wasabb = 1; /* Was abbrev. */
break;
}
}
if (*txp != *tep)
break; /* No match */
tep++; txp++;
}
}
return (find);
}
/* check: check that a date is valid. each of the constraint processing
* routines is called in turn and if any of them do anything then the
* date is invalid.
*/
static check (date)
int date;
{ register int did;
register struct dtm *d = &dtm[date];
if (d->repn != RTM)
return;
did = month (d, d->tm.tm_mon, FUTURE);
if (! did)
did = mday (d, d->tm.tm_mday, FUTURE);
if (! did && d->tm.tm_wday >= 0)
did = weekday (d, d->tm.tm_wday, FUTURE);
if (did)
yyerror ();
return;
}
/* constrain: fill in defaults info in date. con is a dtm containing the
* constraints (or -1 indicating to use ptm). date is the dtm containing
* the date to be constrained. repeat is the loop count. */
static constrain (con, date, past, repeat)
int con, date;
int past, repeat;
{ register int n;
register int did;
register struct tm *c;
register struct dtm *d = &dtm[date];
if (con >= 0)
c = &(dtm[con].tm);
else
c = &ptm;
if (c->tm_year != NOYEAR)
yyerror ();
for (n = 1000, did = 0; ; did = 0)
{ if (c->tm_mon >= 0)
{ did |= month (d, c->tm_mon, past);
}
if (c->tm_mday >= 0)
{ did |= mday (d, c->tm_mday, past);
}
if (c->tm_wday >= 0)
{ did |= weekday (d, c->tm_wday, past);
}
if (! did)
{ if (repeat-- <= 1)
break;
incr (date, past ? -1 : 1);
}
if (--n <= 0)
yyerror ();
}
if (con >= 0)
dtmused[con] = 0;
else
{ ptm.tm_year = NOYEAR;
ptm.tm_mon = -1;
ptm.tm_mday = -1;
ptm.tm_wday = -1;
ptm.tm_yday = -1;
}
}
static time_def (tm, currtm)
struct tm *tm, *currtm;
{ if (shour >= 0) /* Handle simple hour specification */
{ if (tmkey == -1) /* and combine it with time key. */
tmkey = 8; /* Default is 8:00 - 19:59 */
if (shour == 12)
shour = 0;
if (shour < tmkey)
shour += 12;
if (shour < tmkey)
shour += 12;
tm->tm_hour = shour;
}
if (tm->tm_hour >= 0)
{ /* If time specified and fields left out, assume zero. */
if (tm->tm_min < 0)
tm->tm_min = 0;
if (tm->tm_sec < 0)
tm->tm_sec = 0;
}
}
/* date constraint processing routines.
*
* These routines allow determination of the first date after/before
* (but possibly equal to the existing date) which satisfies the given
* constraint(s).
*/
/* The constraints are implemented by calling the appropriate routine(s)
* which check whether the particular constraint is satisfied, and if it
* is not, advances the date until the constraint is satisfied.
*
* The date is stored in the dtm structure.
*/
/* weekday: constrains the day of the week. */
static int weekday (dtm, wkday, past)
struct dtm *dtm;
int wkday; /* 0 = Sunday */
int past; /* true = past */
{ int n;
setrep (dtm, RDAYS);
n = wkday - dtm->days % 7; /* adjustment */
if (past)
{ if (n > 0)
n -= 7;
}
else {
if (n < 0 || (nottoday && n == 0))
n += 7;
nottoday = 0;
}
dtm->days += n;
return (n != 0);
}
/* month: constrains the month to the specified value. */
static int month (dtm, mon, past)
struct dtm *dtm;
int mon;
int past;
{ setrep (dtm, RTM);
if (mon < 0 || mon > 11)
yyerror ();
if (dtm->tm.tm_mon != mon)
{ if (past)
{ if (dtm->tm.tm_mon < mon) /* If earlier month */
dtm->tm.tm_year--; /* Back up a year */
dtm->tm.tm_mday = monthend[mon+1] - monthend[mon];
if (mon == 1 && yearsize (dtm->tm.tm_year+1900) == 365) /* Feb */
dtm->tm.tm_mday--;
}
else
{ if (dtm->tm.tm_mon > mon) /* If later month */
dtm->tm.tm_year++; /* Advance a year */
dtm->tm.tm_mday = 1;
}
dtm->tm.tm_mon = mon;
dtm->tm.tm_wday = dtm->tm.tm_yday = -1;
return (1);
}
return (0);
}
/* mday: constrains the month day to the specified value. Also
* checks the validity of the specified value and, if it is invalid,
* adjusts the month to compensate. */
static int mday (dtm, day, past)
struct dtm *dtm;
int day;
int past;
{ register int maxday;
register int status = 0;
setrep (dtm, RTM);
if (dtm->tm.tm_mday != day)
{ if (past)
{ if (dtm->tm.tm_mday < day) /* Earlier day */
if (dtm->tm.tm_mon-- == 0) /* Back up a month */
{ dtm->tm.tm_mon = 11;
dtm->tm.tm_year--;
}
}
else
{ if (dtm->tm.tm_mday > day) /* Later day */
if (dtm->tm.tm_mon++ == 11) /* Advance month */
{ dtm->tm.tm_mon = 0;
dtm->tm.tm_year++;
}
}
dtm->tm.tm_mday = day;
dtm->tm.tm_wday = dtm->tm.tm_yday = -1;
status = 1;
}
if (day >= 28)
{ maxday = monthend[dtm->tm.tm_mon+1] - monthend[dtm->tm.tm_mon];
if (dtm->tm.tm_mon == 1 && yearsize (dtm->tm.tm_year + 1900) == 365)
maxday--;
if (day > maxday)
{ if (past)
dtm->tm.tm_mday = maxday;
else
{ dtm->tm.tm_mday = 1;
if (dtm->tm.tm_mon++ == 11)
{ dtm->tm.tm_mon = 0;
dtm->tm.tm_year++;
}
}
dtm->tm.tm_wday = dtm->tm.tm_yday = -1;
return (1);
}
}
return (status);
}
/* setrep: sets the representation of the date in a dtm structure. */
static setrep (dtm, rep)
struct dtm *dtm;
int rep;
{ if (dtm->repn == rep)
return;
if (dtm->repn == RDAYS)
{ if (rep == RTM)
days_date (dtm->days, &(dtm->tm));
}
else if (dtm->repn == RTM)
{ if (rep == RDAYS)
dtm->days = date_days (&(dtm->tm));
}
dtm->repn = rep;
return;
}
/* yearsize: returns nuber of days in year. */
static yearsize (year)
int year;
{ return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) ? 366 : 365);
}
/* new_dtm: returns the index of a new dtm structure. If current is CURRTM,
* the structure will contain a copy of the current date-time, else it
* will contain a copy of ptm, and ptm will be reset to all -1.
*/
static int new_dtm (current)
int current;
{ register int i;
for (i = 0; i < NDTMS; i++)
if (! dtmused[i])
{ dtmused[i] = 1;
if (current == CURRTM || current == CURRDATE)
{ dtm[i].tm = *currtm;
if (current == CURRDATE)
{ dtm[i].tm.tm_hour = -1;
dtm[i].tm.tm_min = -1;
dtm[i].tm.tm_sec = -1;
}
dtm[i].repn = RTM;
dtm[i].count = 0;
}
else
{ dtm[i].tm = ptm;
dtm[i].count = pcount;
dtm[i].repn = RTM;
ptm.tm_year = NOYEAR;
ptm.tm_mon = -1;
ptm.tm_mday = -1;
ptm.tm_wday = -1;
ptm.tm_yday = -1;
pcount = 0;
}
return (i);
}
yyerror ();
}
/* incr: increment date in dtm structure. */
static incr (ndtm, days)
int ndtm;
int days;
{ setrep (&dtm[ndtm], RDAYS);
dtm[ndtm].days += days;
}
static incryear (ndtm, years)
int ndtm;
int years;
{ setrep (&dtm[ndtm], RTM);
dtm[ndtm].tm.tm_year += years;
dtm[ndtm].tm.tm_wday = -1; /* Unknown */
dtm[ndtm].tm.tm_yday = -1; /* Unknown */
}
/* incrmonth: increment date in dtm structure by a number of months.*/
static incrmonth (ndtm, months)
int ndtm;
int months;
{ int inc;
inc = months > 0 ? 1 : -1;
setrep (&dtm[ndtm], RTM); /* Use tm structure repn */
for ( ; months != 0; months -= inc)
{ dtm[ndtm].tm.tm_mon += inc;
if (dtm[ndtm].tm.tm_mon < 0)
{ dtm[ndtm].tm.tm_mon = 11;
dtm[ndtm].tm.tm_year--;
}
else if (dtm[ndtm].tm.tm_mon > 11)
{ dtm[ndtm].tm.tm_mon = 0;
dtm[ndtm].tm.tm_year++;
}
}
dtm[ndtm].tm.tm_wday = -1; /* Day of week is unknown */
dtm[ndtm].tm.tm_yday = -1; /* Day of year is unknown */
}